﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace MouseAndCat.Model
{
    internal class MouseHook: IDisposable
    {
        [DllImport("user32.dll")]
        public static extern IntPtr SetCapture(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern int ShowCursor(bool bShow);
        
        [DllImport("user32.dll")]
        public static extern bool ClipCursor(ref RECT lpRect);

        public delegate void MouseMovedToHandler(short x, short y);
        public event MouseMovedToHandler MouseMoved;
        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
        private delegate int LowLevelMouseProc(int code, WindowsMessages wParam, IntPtr lParam);

        //Declare the hook handle as an int.
        static int hHook = 0;

        public const int WH_MOUSE = 7;
        public const int WH_MOUSE_LL = 14;

        //Declare MouseHookProcedure as a HookProc type.
        HookProc MouseHookProcedure;
        LowLevelMouseProc _hookProc;

        [StructLayout(LayoutKind.Sequential)]
        public struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public int mouseData; // be careful, this must be ints, not uints (was wrong before I changed it...). regards, cmew.
            public int flags;
            public int time;
            public UIntPtr dwExtraInfo;
        }

        //Declare the wrapper managed POINT class.
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //Declare the wrapper managed MouseHookStruct class.
        [StructLayout(LayoutKind.Sequential)]
        public struct MouseHookStruct
        {
            public POINT pt;
            public int hwnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;

            /*public static implicit operator Rectangle(RECT rect)
            {
                return Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);
            }

            public static implicit operator RECT(Rectangle rect)
            {
                return new RECT(rect.Left, rect.Top, rect.Right, rect.Bottom);
            }*/

            public RECT(int left, int top, int right, int bottom)
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }
        }


        //This is the Import for the SetWindowsHookEx function.
        //Use this function to install a thread-specific hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //This is the Import for the UnhookWindowsHookEx function.
        //Call this function to uninstall the hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        //This is the Import for the CallNextHookEx function.
        //Use this function to pass the hook information to the next hook procedure in chain.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode,
        IntPtr wParam, IntPtr lParam);

        private bool _mlsc;

        public MouseHook()
        {
            MessageLoopShouldContinue = true;
        }

        public int MouseHookProcLL(int nCode, IntPtr wParam, IntPtr lParam)
        {
            MSLLHOOKSTRUCT ll = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));

            if (nCode < 0)
            {
                return CallNextHookEx(hHook, nCode, wParam, lParam);
            }
            else
            {
                //Create a string variable that shows the current mouse coordinates.
                //String strCaption = "x = " + ll.pt.x.ToString("d") + "  y = " + ll.pt.y.ToString("d");
                //System.Diagnostics.Debug.WriteLine(strCaption);
                MouseMoved((short)ll.pt.x, (short)ll.pt.y);
                // Return non-zero to prevent actions from reaching next procedure

                if (!MessageLoopShouldContinue)
                    return 1;

                return CallNextHookEx(hHook, nCode, wParam, lParam);
            }

            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }

        public void Start()
        {
            MouseHookProcedure = new HookProc(MouseHookProcLL);

            hHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, (IntPtr)0, 0);
            //If the SetWindowsHookEx function fails.
            if (hHook == 0)
                System.Diagnostics.Debug.WriteLine("SetWindowsHookEx Failed");
            else
                System.Diagnostics.Debug.WriteLine("Hooked mouse");
        }

        public void Stop()
        {
            if (hHook != 0)
            {
                if (UnhookWindowsHookEx(hHook))
                {
                    hHook = 0;
                    System.Diagnostics.Debug.WriteLine("UnHooked mouse hook");
                }
             }
        }

        public bool MessageLoopShouldContinue
        {
            get
            {
                return _mlsc;
            }
            set
            {
                _mlsc = value;
            }
        }

        void IDisposable.Dispose()
        {
            Stop();
        }
    }

    
}
